package tp1;

import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Iterator;

// Thread du serveur qui gère chaque client

public class ServeurThread extends Thread{

    public static int nbre_client = 0;
    private static Serveur serveur = null; // Serveur qui crée la thread
    public Utilisateur client = null;
    private BufferedReader in = null;
	private PrintWriter out = null;
    private Socket c_socket;
    private int id_client;
    private boolean continuer = true;

    public ServeurThread(Socket s){
         c_socket = s;
         serveur = Serveur.getInstance();
         nbre_client++;
         id_client = nbre_client;
     }
    
    public void run() {
        System.out.println("Client accepté. ID : " + id_client +" Adresse : "+c_socket.getRemoteSocketAddress()+" port : "+c_socket.getPort()+". Il y a " + ServeurThread.nbre_client + " clients connectés.");
        
    	try {
			in = new BufferedReader(new InputStreamReader(c_socket.getInputStream()));
			out = new PrintWriter(new OutputStreamWriter(c_socket.getOutputStream()));

			while (continuer) {

				String clientCommande;
				String reponseServeur;
                boolean majGroupe = false;
                boolean majUtilisateur = false;


                while(!in.ready()) {
                    sleep(10);
                }

                clientCommande = in.readLine();
				
				System.out.println("Le client a dit :" + clientCommande);
				// le client veut se déconnecter
				if (clientCommande.length()> 2 && clientCommande.substring(0,3).equalsIgnoreCase("end")) {
					// Quitter ce thread
					continuer = false;
					reponseServeur = "Au revoir.";
					nbre_client--;
					System.out.println("Connexion fermée à la demande du client.");
                    serveur.utilisateurs.remove(client); // supprime l'utilisateur de la liste
                    Thread.currentThread().interrupt();
                    return;
				}
				// le client veut créer un groupe
				else if(clientCommande.length()> 3 && clientCommande.substring(0, 4).equalsIgnoreCase("cgr "))
				{
					String nomGroupe = String.format("%-10s", String.format("%.10s", clientCommande.substring(4)));
					if(!nomGroupe.isEmpty() )
					{
						Iterator<Groupe> it = serveur.groupes.iterator();
						boolean dejaExistant = false;
					    while(it.hasNext())
					    {
					    	if(it.next().getNom().equalsIgnoreCase(nomGroupe))
					    		dejaExistant = true;
					    }
					    
						if(dejaExistant)
						{
							// groupe déja existant
							reponseServeur = "Groupe non crée, déjà existant.";
							System.out.println("Le groupe n'a pas été ajouté.");
						}
						else
						{
                            //TODO : comment choisir une adresse multicast ? plus ou moins OK
							//InetAddress multicastAddress = InetAddress.getByName("224.0.0.1");
                            InetAddress multicastAddress = InetAddress.getByName(serveur.adresseMulticast);


							Groupe g = new Groupe(nomGroupe,
									multicastAddress,serveur.multicastPort);
							serveur.groupes.add(g);
							reponseServeur = "Groupe OK.";
                            System.out.println("Groupe " + nomGroupe + "  créé. Adresse multicast "+multicastAddress.getHostAddress()+" port "+serveur.multicastPort);
                            majGroupe = true;
                            serveur.multicastPort++;
                        }
					}
					else
					{
						reponseServeur = "Nom de groupe vide, veuillez réessayer.";
					}
				}
				// le client veut se connecter avec un identifiant
				else if(clientCommande.length()> 3 && clientCommande.substring(0,3).equalsIgnoreCase("idt"))
				{
					String identifiant = String.format("%-6s",clientCommande.substring(3,9));
                    //InetAddress adresse = c_socket.getInetAddress();
                    InetAddress adresse = c_socket.getInetAddress();

                    int port = Integer.parseInt(clientCommande.substring(9,14));
				
					if(!identifiant.isEmpty() )
					{	
						Iterator<Utilisateur> it = serveur.utilisateurs.iterator();
						boolean dejaExistant = false;
					    while(it.hasNext())
					    {
                            Utilisateur u= it.next();
					    	if(u.getIdentifiant().equalsIgnoreCase(identifiant))
					    		dejaExistant = true;
                            if(u.getAdresse().equals(adresse) && u.getPort() == port) { // l'adresse est déjà présente
                                System.out.println("Adresse déjà présente, mise à jour de l'identifiant.");
                                u.setIdentifiant(identifiant);
                                dejaExistant = true;
                                majUtilisateur = true;
                            }

					    }
						if(dejaExistant)
						{
							// utilisateur deja la
							reponseServeur = "IDK"; // Identifiant déjà pris, utilisateur non ajouté."; // l'identifiant est deja pris
							System.out.println("Identifiant déjà présent. L'utilisateur n'a pas été ajouté.");
						}
						else
						{
							client = new Utilisateur(identifiant,
									adresse, port); //
							serveur.utilisateurs.add(client); 
							reponseServeur = "IDO"; // Identifiant OK.";
                            System.out.println("Utilisateur " + identifiant + " ajouté");
                            majUtilisateur = true;
                            majGroupe = true;
                            // commence à envoyer les màj utilisateurs et groupes. Si on le fait avant on a le problème d'authentification
                            Thread t2 = new ServeurListes(c_socket);
                            t2.start();
                        }
					}
					else
						reponseServeur = "IDK"; // Identifiant vide, veuillez réessayer.";
				}
				
				// le client veut rejoindre un groupe
				else if(clientCommande.length()> 3 && clientCommande.substring(0, 4).equalsIgnoreCase("rgr "))
				{
					// le client s'occupe d'établir la connexion avec le groupe, le serveur doit donner l'adresse multicast
					String nomGroupe = String.format("%-10s", String.format("%.10s", clientCommande.substring(4)));
					if(!nomGroupe.isEmpty() )
					{
						Groupe aRejoindre = null;
                        Groupe groupeCourant = null;

						Iterator<Groupe> it = serveur.groupes.iterator();
						boolean groupeExiste = false;
					    while(it.hasNext())
					    {	groupeCourant = it.next();
					    	if(groupeCourant.getNom().equals(nomGroupe))
					    		{
					    			groupeExiste = true;
					    			aRejoindre = new Groupe(groupeCourant.getNom(),groupeCourant.getAdresseMulticast(),groupeCourant.getPort());
					    		}
					    }
					    
						if(groupeExiste)
						{
							// construire la réponse (rgo : rejoindre groupe ok)
                            String [] adresse = aRejoindre.getAdresseMulticast().getHostAddress().split("\\.");
                            String port = String.format("%05d", aRejoindre.getPort());
                            for (int i = 0; i < adresse.length; i++) {
                                adresse[i] = String.format("%03d", Integer.parseInt(adresse[i])); //formatte l'adresse sous la forme 000.000.000.000
                            }
                            String adresseGroupe = adresse[0] + "." + adresse[1] + "." + adresse[2] + "." + adresse[3]; // met l'adresse au bon format
                            reponseServeur = "rgo" + aRejoindre.getNom() + adresseGroupe + port;
							System.out.println("Coordonnées du groupe envoyées.");
						}
						else
						{
                            reponseServeur = "RGK";
                            System.out.println("Groupe "+serveur.ANSI_RED+nomGroupe+serveur.ANSI_RESET+" non existant.");
						}
					}
					else
					{
                        reponseServeur = "RGK";
                        serveur.debug("Nom de groupe vide, veuillez réessayer.");
					}
				}

				else 
				{	
					reponseServeur =  "Cmds: END : déconnexion, " +
							"IDT <identifiant> : connexion avec identifiant (6 char)," +
							"CGR <groupe> : création de groupe, RGR <groupe> : rejoindre groupe. ";
				}
				out.println(reponseServeur);
				out.flush();
                if (majUtilisateur) {
                    envoyer_utilisateurs();
                    //TODO : mettre à jour tous les clients connectés
                }
                if (majGroupe) {
                    envoyer_groupes();
                    //TODO : mettre à jour tous les clients connectés
                }
            }
		} catch (Exception e) {
			e.printStackTrace();
            serveur.utilisateurs.remove(client); // supprime l'utilisateur de la liste
        } finally {
			// Nettoyage
			try {
				in.close();
				out.close();
				c_socket.close();
				System.out.println("C'est fini pour ce client.");
                serveur.utilisateurs.remove(client); // supprime l'utilisateur de la liste
                System.out.println("Utilisateur " + client.getIdentifiant() + " supprimé.");
                envoyer_utilisateurs();
            } catch (IOException io_Exception) {
				io_Exception.printStackTrace();
                serveur.utilisateurs.remove(client); // supprime l'utilisateur de la liste

            }
        }
    }


    protected void envoyer_utilisateurs() {
        System.out.println(serveur.ANSI_RED+"[envoyer_utilisateurs()]"+serveur.ANSI_RESET);
        // Envoi de la liste d'utilisateurs
        String listeUtilisateurs = "", nom, port, message;
        String[] adresse;
        int count = 0;

        Iterator<Utilisateur> it = serveur.utilisateurs.iterator();
        Utilisateur u;

        System.out.println("=====================================");
        System.out.println("Liste d'utilisateurs : ");
        System.out.println("Id.       | Adresse       |Port ");
        System.out.println("----------+---------------+-----");
        while (it.hasNext()) {
            count++;
            u = it.next();
            nom = String.format("%-6s", u.getIdentifiant());
            port = String.format("%05d", u.getPort()); //formatte le port sur 5 caractères
            adresse = u.getAdresse().getHostAddress().split("\\.");
            for (int i = 0; i < adresse.length; i++) {
                adresse[i] = String.format("%03d", Integer.parseInt(adresse[i])); //formatte l'adresse sous la forme 000.000.000.000
            }
            System.out.println(nom + "|" + adresse[0] + "." + adresse[1] + "." + adresse[2] + "." + adresse[3] + "|" + port);
            listeUtilisateurs += adresse[0] + "." + adresse[1] + "." + adresse[2] + "." + adresse[3] + port + nom;
        }
        System.out.println("=====================================");
        message = "LUT" + String.format("%02d", count) + listeUtilisateurs;

        out.println(message);
        out.flush();

    }

    protected void envoyer_groupes() {
        System.out.println(serveur.ANSI_RED+"[envoyer_groupes()]"+serveur.ANSI_RESET);
        //Envoi de la liste de groupes
        String listeGroupes = "", nom, port, message;
        String[] adresse;

        Iterator<Groupe> itg = serveur.groupes.iterator();
        Groupe g;
        int count = 0;

        System.out.println("=====================================");
        System.out.println("Liste de groupes : ");
        while (itg.hasNext()) {
            count++;
            g = itg.next();
            nom = String.format("%-10s", g.getNom());
            port = String.format("%05d", g.getPort()); //formatte le port sur 5 caractères
            adresse = g.getAdresseMulticast().getHostAddress().split("\\.");
            System.out.println("Groupe n°" + count + " Nom : " + nom + " Port : " + port);
            for (int i = 0; i < adresse.length; i++) {
                adresse[i] = String.format("%03d", Integer.parseInt(adresse[i])); //formatte l'adresse sous la forme 000.000.000.000
            }
            listeGroupes += adresse[0] + "." + adresse[1] + "." + adresse[2] + "." + adresse[3] + port + nom;
        }
        System.out.println("=====================================");

        message = "LGR" + String.format("%02d", count) + listeGroupes;

        out.println(message);
        out.flush();
    }
	

}
